 
#import "WWTextField.h"
#import "WWTextFieldCell.h"
#import "WWTCLInterp.h"

#import "avoidStupidWarnings.h"

@implementation WWTextFieldCell


+ initialize { [WWTextFieldCell setVersion:3]; return self; }
////////////////////////////////////////////////////////
//
- init
{
  [super init];
  controlStringSize = 32;
  controlString = (char *)NXZoneCalloc([self zone], controlStringSize, sizeof(char));
  sprintfControlString = (char *)NXZoneCalloc([self zone], controlStringSize, sizeof(char));
  tclExpressionSize = 32;
  tclExpression = (char *)NXZoneCalloc([self zone], tclExpressionSize, sizeof(char));
  tclVarSize = 32;
  tclVar = (char *)NXZoneCalloc([self zone], tclVarSize, sizeof(char));
  tclCommandSize = 32;
  tclCommand = (char *)NXZoneCalloc([self zone], tclCommandSize, sizeof(char));
  interp = nil;
  tclCommandDirty = YES;

  return self;
}
//
- awake
{
  [super awake];
  tclCommandDirty = YES;
  return self;
}

- free
{
  if (controlString) { NXZoneFree([self zone], controlString); }
  if (sprintfControlString) { NXZoneFree([self zone], sprintfControlString); }
  if (tclExpression) { NXZoneFree([self zone], tclExpression); }
  if (tclVar) { NXZoneFree([self zone], tclVar); }
  if (tclCommand) { NXZoneFree([self zone], tclCommand); }
  return [super free];
}

- reinitializeControlStringAndTclVarFromZone:(NXZone *)zone
{
  controlString = (char *)NXZoneCalloc(zone, controlStringSize, sizeof(char));
  sprintfControlString = (char *)NXZoneCalloc(zone, controlStringSize, sizeof(char));
  tclExpression = (char *)NXZoneCalloc(zone, tclExpressionSize, sizeof(char));
  tclVar = (char *)NXZoneCalloc(zone, tclVarSize, sizeof(char));
  tclCommand = (char *)NXZoneCalloc(zone, tclCommandSize, sizeof(char));
  return self;
}

- copyFromZone:(NXZone *)zone
{
  id newCopy = [super copyFromZone:zone];


  [newCopy reinitializeControlStringAndTclVarFromZone:zone];
  [newCopy setControlString:controlString];
  [newCopy setTclExpression:tclExpression];
  [newCopy setTclVar:tclVar];
  [newCopy setTclCommand:tclCommand];

  return newCopy;
}

- setStringValue:(const char *)str
{
  [super setStringValue:str];
  tclCommandDirty = YES;
  return self;
}

- setIntValue:(int)val
{
  [super setIntValue:val];
  tclCommandDirty = YES;
  return self;
}

- setFloatValue:(float)val
{
  [super setFloatValue:val];
  tclCommandDirty = YES;
  return self;
}

- setDoubleValue:(double)val
{
  [super setDoubleValue:val];
  tclCommandDirty = YES;
  return self;
}

- setInterp:newInterp { interp = newInterp; return self; }
- interp { return interp; }

- resizeTclCommandForArgVector:(char *)argOrderVector
{
  BOOL    done = NO;


  // this case handles all single numbers.  We figure the max printable length of a number is 32
  if (!done && (!strcmp("d", argOrderVector) || !strcmp("f", argOrderVector) || !strcmp("S", argOrderVector)))
  {  while (tclCommandSize < (controlStringSize + 32))
     {  if (!tclCommandSize)
	{  tclCommandSize = 32;
           tclCommand = (char *)NXZoneCalloc([self zone], tclCommandSize, sizeof(char));
        }
        else
        {  tclCommandSize *= 2;
           tclCommand = (char *)NXZoneRealloc([self zone], tclCommand, tclCommandSize);
        }
     }
     done = YES;
  }

  // this case handles three numbers
  if (!done && (!strcmp("fff", argOrderVector) || !strcmp("RGB", argOrderVector)))
  {  while (tclCommandSize  < (controlStringSize + (2 + (3 * 32))))
     {  if (!tclCommandSize)
	{  tclCommandSize = 32;
           tclCommand = (char *)NXZoneCalloc([self zone], tclCommandSize, sizeof(char));
        }
        else
        {  tclCommandSize *= 2;
           tclCommand = (char *)NXZoneRealloc([self zone], tclCommand, tclCommandSize);
        }
     }
     done = YES;
  }

  // these two cases handle single strings
  if (!done && (!strcmp("s", argOrderVector)))
  {  const char  *str = [self stringValue]; 
     int   strLen = 1 + strlen(str);

     while (tclCommandSize < (controlStringSize + strLen))
     {  if (!tclCommandSize)
	{  tclCommandSize = 32;
           tclCommand = (char *)NXZoneCalloc([self zone], tclCommandSize, sizeof(char));
        }
        else
        {  tclCommandSize *= 2;
           tclCommand = (char *)NXZoneRealloc([self zone], tclCommand, tclCommandSize);
        }
     }
     done = YES;
  }

  if (!done && (!strcmp("F", argOrderVector)))
  {  const char  *str = [[self font] name]; 
     int   strLen = 1 + strlen(str);

     while (tclCommandSize < (controlStringSize + strLen))
     {  if (!tclCommandSize)
	{  tclCommandSize = 32;
           tclCommand = (char *)NXZoneCalloc([self zone], tclCommandSize, sizeof(char));
        }
        else
        {  tclCommandSize *= 2;
           tclCommand = (char *)NXZoneRealloc([self zone], tclCommand, tclCommandSize);
        }
     }
     done = YES;
  }

  // those were the easy ones; now for the permutations...

  // two strings
  if (!done && (!strcmp("Fs", argOrderVector) || !strcmp("sF", argOrderVector)))
  {  const char  *str1 = [[self font] name]; 
     const char  *str2 = [self stringValue];
     int   strLen = 2 + strlen(str1) + strlen(str2);

     while (tclCommandSize < (controlStringSize + strLen))
     {  if (!tclCommandSize)
	{  tclCommandSize = 32;
           tclCommand = (char *)NXZoneCalloc([self zone], tclCommandSize, sizeof(char));
        }
        else
        {  tclCommandSize *= 2;
           tclCommand = (char *)NXZoneRealloc([self zone], tclCommand, tclCommandSize);
        }
     }
     done = YES;
  }

  // a string and a number
  if (!done && (   !strcmp("sf", argOrderVector) || !strcmp("fs", argOrderVector)
                || !strcmp("sd", argOrderVector) || !strcmp("ds", argOrderVector)
                || !strcmp("sS", argOrderVector) || !strcmp("Ss", argOrderVector)))
  {  const char  *str = [self stringValue];
     int   strLen = 1 + strlen(str);


     while (tclCommandSize < (controlStringSize + strLen + 32))
     {  if (!tclCommandSize)
	{  tclCommandSize = 32;
           tclCommand = (char *)NXZoneCalloc([self zone], tclCommandSize, sizeof(char));
        }
        else
        {  tclCommandSize *= 2;
           tclCommand = (char *)NXZoneRealloc([self zone], tclCommand, tclCommandSize);
        }
     }
     done = YES;
  }

  if (!done && (   !strcmp("fF", argOrderVector) || !strcmp("Ff", argOrderVector) 
                || !strcmp("dF", argOrderVector) || !strcmp("Fd", argOrderVector)
                || !strcmp("SF", argOrderVector) || !strcmp("FS", argOrderVector)))
  {  const char  *str = [[self font] name]; 
     int   strLen = 1 + strlen(str);

     while (tclCommandSize < (controlStringSize + strLen + 32))
     {  if (!tclCommandSize)
	{  tclCommandSize = 32;
           tclCommand = (char *)NXZoneCalloc([self zone], tclCommandSize, sizeof(char));
        }
        else
        {  tclCommandSize *= 2;
           tclCommand = (char *)NXZoneRealloc([self zone], tclCommand, tclCommandSize);
        }
     }
     done = YES;
  }

  // two strings, one number
  if (!done && (   !strcmp("Fsd", argOrderVector) || !strcmp("sFd", argOrderVector)
                || !strcmp("Fsf", argOrderVector) || !strcmp("sFf", argOrderVector)
                || !strcmp("FsS", argOrderVector) || !strcmp("sFS", argOrderVector)
                || !strcmp("dFs", argOrderVector) || !strcmp("dsF", argOrderVector)
                || !strcmp("fFs", argOrderVector) || !strcmp("fsF", argOrderVector)
                || !strcmp("SFs", argOrderVector) || !strcmp("SsF", argOrderVector)
                || !strcmp("Fds", argOrderVector) || !strcmp("sdF", argOrderVector)
                || !strcmp("Ffs", argOrderVector) || !strcmp("sfF", argOrderVector)
                || !strcmp("FSs", argOrderVector) || !strcmp("sSF", argOrderVector)))
  {  const char  *str1 = [[self font] name]; 
     const char  *str2 = [self stringValue];
     int   strLen = 2 + strlen(str1) + strlen(str2);

     while (tclCommandSize < (controlStringSize + strLen + 32))
     {  if (!tclCommandSize)
	{  tclCommandSize = 32;
           tclCommand = (char *)NXZoneCalloc([self zone], tclCommandSize, sizeof(char));
        }
        else
        {  tclCommandSize *= 2;
           tclCommand = (char *)NXZoneRealloc([self zone], tclCommand, tclCommandSize);
        }
     }
     done = YES;
  }

  return self;
}


- updateTclCommand
{
  char     *controlStringPtr, *sprintfControlStringPtr;
  BOOL     done;
  int      argOrderSize = 32;
  char     *argOrderVector = (char *)NXZoneCalloc([self zone], argOrderSize, sizeof(char));
  int      argOrderOffset = 0;
  NXColor  color = [self textColor];


  if (!controlString)
  {  return nil;
  }

  // we want to grovel over the controlString, 
  // for right now, we really only tend to have the following patterns 
  // in the controlString: 1 %f, 1 %d, 1 %s, %R, %G, %B
  // some new ones for doing string stuff: %F %S %s, and %F %s
  controlStringPtr = controlString;
  sprintfControlStringPtr = sprintfControlString;
  strcpy(sprintfControlString, controlString);
  while (*controlStringPtr)
  {  if (*controlStringPtr == '%')
     {  sprintfControlStringPtr++;  controlStringPtr++;
        if (*controlStringPtr)
        {  switch (*controlStringPtr)
           {  case 'd':	while (argOrderOffset >= argOrderSize)
                        {  argOrderSize *= 2;
                           argOrderVector = (char *)NXZoneRealloc([self zone], argOrderVector, argOrderSize);
                        }
                        argOrderVector[argOrderOffset++] = 'd';
                        sprintfControlStringPtr++; controlStringPtr++;
			break;
              case 'f':	while (argOrderOffset >= argOrderSize)
                        {  argOrderSize *= 2;
                           argOrderVector = (char *)NXZoneRealloc([self zone], argOrderVector, argOrderSize);
                        }
                        argOrderVector[argOrderOffset++] = 'f';
                        sprintfControlStringPtr++; controlStringPtr++;
			break;
              case 'S':	while (argOrderOffset >= argOrderSize)
                        {  argOrderSize *= 2;
                           argOrderVector = (char *)NXZoneRealloc([self zone], argOrderVector, argOrderSize);
                        }
                        argOrderVector[argOrderOffset++] = 'S'; *sprintfControlStringPtr++ = 'f';
                        sprintfControlStringPtr++; controlStringPtr++;
			break;
              case 's':	while (argOrderOffset >= argOrderSize)
                        {  argOrderSize *= 2;
                           argOrderVector = (char *)NXZoneRealloc([self zone], argOrderVector, argOrderSize);
                        }
                        argOrderVector[argOrderOffset++] = 's';
                        sprintfControlStringPtr++; controlStringPtr++;
			break;
              case 'F':	while (argOrderOffset >= argOrderSize)
                        {  argOrderSize *= 2;
                           argOrderVector = (char *)NXZoneRealloc([self zone], argOrderVector, argOrderSize);
                        }
                        argOrderVector[argOrderOffset++] = 'F'; *sprintfControlStringPtr++ = 's';
                        controlStringPtr++;
			break;
              case 'R':	while (argOrderOffset >= argOrderSize)
                        {  argOrderSize *= 2;
                           argOrderVector = (char *)NXZoneRealloc([self zone], argOrderVector, argOrderSize);
                        }
                        argOrderVector[argOrderOffset++] = 'R';
                        *sprintfControlStringPtr++ = 'f'; controlStringPtr++;
                        break;
              case 'G':	while (argOrderOffset >= argOrderSize)
                        {  argOrderSize *= 2;
                           argOrderVector = (char *)NXZoneRealloc([self zone], argOrderVector, argOrderSize);
                        }
                        argOrderVector[argOrderOffset++] = 'G';
                        *sprintfControlStringPtr++ = 'f'; controlStringPtr++;
                        break;
              case 'B':	while (argOrderOffset >= argOrderSize)
                        {  argOrderSize *= 2;
                           argOrderVector = (char *)NXZoneRealloc([self zone], argOrderVector, argOrderSize);
                        }
                        argOrderVector[argOrderOffset++] = 'B'; *sprintfControlStringPtr++ = 'f';
                        controlStringPtr++;
                        break;
              case '%':	sprintfControlStringPtr++; controlStringPtr++;
                        break;
               default:	NXLogError("WWSliderCell: unknown specifier <%c> in controlString.\n", *controlStringPtr);
                        sprintfControlStringPtr++; controlStringPtr++;
			break;
           }
       }
     }
     else
     {  sprintfControlStringPtr++;  controlStringPtr++;
     }
  }
  argOrderVector[argOrderOffset] = 0;

  done = NO;

  // since strings are really the only variable length thing that
  // could get shoved into the tclCommand, we really should have a flag in
  // the above switch in case we don't encounter any tags that correspond
  // to strings.  If we don't, it's wicked easy to grow the tclCommand
  // buffer...

  if (!done && !strcmp("d", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, [self intValue]);
     done = YES;
  }
  if (!done && !strcmp("f", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, [self floatValue]);
     done = YES;
  }
  if (!done && !strcmp("s", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, [self stringValue]);
     done = YES;
  }

  if (!done && !strcmp("FSs", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, [[self font] name], [[self font] pointSize], [self stringValue]);
     done = YES;
  }

  if (!done && !strcmp("Fs", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, [[self font] name], [self stringValue]);
     done = YES;
  }

  // for back compatibility
  if (!done && !strcmp("fff", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, NXRedComponent(color), NXGreenComponent(color), NXBlueComponent(color));
     done = YES;
  }
  if (!done && !strcmp("RGB", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, NXRedComponent(color), NXGreenComponent(color), NXBlueComponent(color));
     done = YES;
  }
  if (!done && !strcmp("fRGB", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, [self floatValue], NXRedComponent(color), NXGreenComponent(color), NXBlueComponent(color));
     done = YES;
  }
  if (!done && !strcmp("RGBf", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, NXRedComponent(color), NXGreenComponent(color), NXBlueComponent(color), [self floatValue]);
     done = YES;
  }
  if (!done && !strcmp("dRGB", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, [self intValue], NXRedComponent(color), NXGreenComponent(color), NXBlueComponent(color));
     done = YES;
  }
  if (!done && !strcmp("RGBd", argOrderVector))
  {  [self resizeTclCommandForArgVector:argOrderVector];
     sprintf(tclCommand, sprintfControlString, NXRedComponent(color), NXGreenComponent(color), NXBlueComponent(color), [self intValue]);
     done = YES;
  }

  if (!done)
  {  while (tclCommandSize < controlStringSize)
     {  if (!tclCommandSize)
	{  tclCommandSize = 32;
           tclCommand = (char *)NXZoneCalloc([self zone], tclCommandSize, sizeof(char));
        }
        else
        {  tclCommandSize *= 2;
           tclCommand = (char *)NXZoneRealloc([self zone], tclCommand, tclCommandSize);
        }
     }
     strcpy(tclCommand, controlString);
  }
  tclCommandDirty = NO;

  NXZoneFree([self zone], argOrderVector);

  return self;
}


- evaluateSelf
{
   int   ret;
   char  *newStringValue;
   id    retID;


  newStringValue = [interp globalEval:tclExpression :&ret];
  if ((ret == TCL_OK) || (ret == TCL_RETURN))
  {  [self setStringValue:newStringValue];
     retID = self;
  }
  else
  {  NXLogError("WWSliderCell: error <%s> evaluating tcl expression <%s>\n", 
		newStringValue, tclExpression);
     if (newStringValue) { free(newStringValue); }
     retID = nil;
  }
  return retID;
}

- updateInterp
{
  if ([interp eval:[self tclCommand]])
  {  return self;
  }
  return nil;
}


- setControlString:(const char *)str 
{ 
  int   cnt;


  if (!str) { return self; }
  if (controlStringSize <= 0) 
  {  controlStringSize = 32; 
     controlString = (char *)NXZoneCalloc([self zone], controlStringSize, sizeof(char));
  }
  cnt = strlen(str);
  while (cnt >= controlStringSize)
  {  controlStringSize *= 2;
     controlString = (char *)NXZoneRealloc([self zone], controlString, controlStringSize);
  }
  if (sprintfControlString)
  {  sprintfControlString = (char *)NXZoneRealloc([self zone], sprintfControlString, controlStringSize);
  }
  else
  {  sprintfControlString = (char *)NXZoneCalloc([self zone], controlStringSize, sizeof(char));
  }
  strcpy(controlString, str); 
  tclCommandDirty = YES;
  return self; 
}
//
- (const char *)controlString { return controlString; }

- setTclExpression:(const char *)str 
{ 
  int   cnt;


  if (!str) { return self; }
  if (tclExpressionSize <= 0) 
  {  tclExpressionSize = 32; 
     tclExpression = (char *)NXZoneCalloc([self zone], tclExpressionSize, sizeof(char));
  }
  cnt = strlen(str);
  while (cnt >= tclExpressionSize)
  {  tclExpressionSize *= 2;
     tclExpression = (char *)NXZoneRealloc([self zone], tclExpression, tclExpressionSize);
  }
  strcpy(tclExpression, str); 
  return self; 
}
//
- (const char *)tclExpression { return tclExpression; }

- setTclVar:(const char *)str 
{ 
  int   cnt;


  if (!str) { return self; }
  if (tclVarSize <= 0) 
  {  tclVarSize = 32; 
     tclVar = (char *)NXZoneCalloc([self zone], tclVarSize, sizeof(char));
  }
  cnt = strlen(str);
  while (cnt >= tclVarSize)
  {  tclVarSize *= 2;
     tclVar = (char *)NXZoneRealloc([self zone], tclVar, tclVarSize);
  }
  strcpy(tclVar, str); 
  return self; 
}
//
- (const char *)tclVar { return tclVar; }

- setTclCommand:(const char *)str 
{ 
  int   cnt;


  if (!str) { return self; }
  if (tclCommandSize <= 0) 
  {  tclCommandSize = 32; 
     tclCommand = (char *)NXZoneCalloc([self zone], tclCommandSize, sizeof(char));
  }
  cnt = strlen(str);
  while (cnt >= tclCommandSize)
  {  tclCommandSize *= 2;
     tclCommand = (char *)NXZoneRealloc([self zone], tclCommand, tclCommandSize);
  }
  strcpy(tclCommand, str); 
  return self; 
}
//
- (const char *)tclCommand 
{ 
  if (tclCommandDirty)
  {  [self updateTclCommand];
  }
  return tclCommand; 
}

- write:(NXTypedStream *)stream 
{
   [super write:stream];
   NXWriteType(stream, "*", &controlString);
   NXWriteType(stream, "*", &tclVar);
   NXWriteType(stream, "*", &tclExpression);
   NXWriteType(stream, "*", &tclCommand);
   NXWriteObjectReference(stream, interp);
   return self;
}
//
- read:(NXTypedStream *)stream 
{
   int version;

   [super read:stream];

   version = NXTypedStreamClassVersion(stream, "WWTextFieldCell");
   if (version == 1)
   {  NXReadType(stream, "*", &controlString);
      if (controlString) 
      {  controlStringSize = strlen(controlString) + 1;
         sprintfControlString = (char *)NXZoneCalloc([self zone], controlStringSize, sizeof(char));
      }
      else
      {  controlStringSize = 0;
         sprintfControlString = NULL;
      }
      NXReadType(stream, "*", &tclVar);
      if (tclVar) 
      {  tclVarSize = strlen(tclVar) + 1;
      }
      else
      { tclVarSize = 0;
      }
      tclExpressionSize = 32;
      tclExpression = (char *)NXZoneCalloc([self zone], tclExpressionSize, sizeof(char));
      interp = nil;
      tclCommandSize = 0;
      tclCommand = NULL;
   }
   if (version == 2)
   {  NXReadType(stream, "*", &controlString);
      if (controlString) 
      {  controlStringSize = strlen(controlString) + 1;
         sprintfControlString = (char *)NXZoneCalloc([self zone], controlStringSize, sizeof(char));
      }
      else
      {  controlStringSize = 0;
         sprintfControlString = NULL;
      }
      NXReadType(stream, "*", &tclVar);
      if (tclVar) 
      {  tclVarSize = strlen(tclVar) + 1;
      }
      else
      { tclVarSize = 0;
      }
      NXReadType(stream, "*", &tclExpression);
      if (tclExpression) 
      {  tclExpressionSize = strlen(tclExpression) + 1;
      }
      else
      {  tclExpressionSize = 0;
      }
      interp = nil;
      tclCommandSize = 0;
      tclCommand = NULL;
   }
   if (version == 3)
   {  NXReadType(stream, "*", &controlString);
      if (controlString) 
      {  controlStringSize = strlen(controlString) + 1;
         sprintfControlString = (char *)NXZoneCalloc([self zone], controlStringSize, sizeof(char));
      }
      else
      {  controlStringSize = 0;
         sprintfControlString = NULL;
      }
      NXReadType(stream, "*", &tclVar);
      if (tclVar) 
      {  tclVarSize = strlen(tclVar) + 1;
      }
      else
      { tclVarSize = 0;
      }
      NXReadType(stream, "*", &tclExpression);
      if (tclExpression) 
      {  tclExpressionSize = strlen(tclExpression) + 1;
      }
      else
      {  tclExpressionSize = 0;
      }
      NXReadType(stream, "*", &tclCommand);
      if (tclCommand) 
      {  tclCommandSize = strlen(tclCommand) + 1;
      }
      else
      {  tclCommandSize = 0;
      }
      interp = NXReadObject(stream);
   }
   return self;
}

// IB stuff
- (const char *)getInspectorClassName 
{  
   NXEvent  *event = [NXApp currentEvent];

   if (event->flags & NX_ALTERNATEMASK)
   {  return [super getInspectorClassName];
   }
   
   return "WWTextFieldCellIBInspector"; 
}

@end
